<!DOCTYPE html>

<html>
<head>
<title>Building Admin - SEO - QOR5 Document</title>

<meta name='description'>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<base href='/docs/'>

<link href='index.css' rel='stylesheet' type='text/css'>

<script type='text/javascript' defer src='index.js'></script>
</head>

<body>
<div id='app' v-cloak>
<div v-init-context:vars='{hideAside: false}' class='flex h-screen'>
<div class='flex-1 flex flex-col overflow-hidden'>
<div class='flex h-full'>
<aside v-show='!vars.hideAside' id='menuScroller' class='flex flex-col w-80 h-full bg-gray-50 border-r border-gray-200 overflow-y-auto'>
<div class='h-12'><search></search></div>

<ul class='px-0 py-3 mx-0 text-base font-normal list-none text-gray-700'>
<li class='m-0'>
<a href='index.html' id='index.html' onclick='window.storeMenuState("index.html")' class='inline-block px-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Introduction</a>
</li>

<li class='cursor-default px-4 py-1 truncate break-words w-64 m-0'>Getting Started</li>

<li class='m-0'>
<a href='getting-started/one-minute-quick-start.html' id='getting-started/one-minute-quick-start.html' onclick='window.storeMenuState("getting-started/one-minute-quick-start.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>1 Minute Quick Start</a>
</li>

<li class='cursor-default px-4 py-1 truncate break-words w-64 m-0'>Building Admin</li>

<li class='m-0'>
<a href='basics/presets-instant-crud.html' id='basics/presets-instant-crud.html' onclick='window.storeMenuState("basics/presets-instant-crud.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>presets, Instant CRUD</a>
</li>

<li class='m-0'>
<a href='basics/listing.html' id='basics/listing.html' onclick='window.storeMenuState("basics/listing.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Listing</a>
</li>

<li class='m-0'>
<a href='basics/listing-customizations.html' id='basics/listing-customizations.html' onclick='window.storeMenuState("basics/listing-customizations.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Listing Customizations</a>
</li>

<li class='m-0'>
<a href='basics/filter.html' id='basics/filter.html' onclick='window.storeMenuState("basics/filter.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Filters</a>
</li>

<li class='m-0'>
<a href='presets-guide/editing-customizations.html' id='presets-guide/editing-customizations.html' onclick='window.storeMenuState("presets-guide/editing-customizations.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Editing</a>
</li>

<li class='m-0'>
<a href='basics/brand.html' id='basics/brand.html' onclick='window.storeMenuState("basics/brand.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Brand</a>
</li>

<li class='m-0'>
<a href='basics/menu.html' id='basics/menu.html' onclick='window.storeMenuState("basics/menu.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Menu</a>
</li>

<li class='m-0'>
<a href='presets-guide/detail-page-for-complex-object.html' id='presets-guide/detail-page-for-complex-object.html' onclick='window.storeMenuState("presets-guide/detail-page-for-complex-object.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Detailing</a>
</li>

<li class='m-0'>
<a href='basics/layout.html' id='basics/layout.html' onclick='window.storeMenuState("basics/layout.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Layout</a>
</li>

<li class='m-0'>
<a href='basics/login.html' id='basics/login.html' onclick='window.storeMenuState("basics/login.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Login</a>
</li>

<li class='m-0'>
<a href='presets-guide/permissions.html' id='presets-guide/permissions.html' onclick='window.storeMenuState("presets-guide/permissions.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Permissions</a>
</li>

<li class='m-0'>
<a href='presets-guide/role.html' id='presets-guide/role.html' onclick='window.storeMenuState("presets-guide/role.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Role</a>
</li>

<li class='m-0'>
<a href='basics/shortcut.html' id='basics/shortcut.html' onclick='window.storeMenuState("basics/shortcut.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Keyboard Shortcut</a>
</li>

<li class='m-0'>
<a href='basics/confirm-dialog.html' id='basics/confirm-dialog.html' onclick='window.storeMenuState("basics/confirm-dialog.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Confirm Dialog</a>
</li>

<li class='m-0'>
<a href='slug.html' id='slug.html' onclick='window.storeMenuState("slug.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Slug</a>
</li>

<li class='m-0'>
<a href='seo.html' id='seo.html' onclick='window.storeMenuState("seo.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-blue-500'>SEO</a>
</li>

<li class='m-0'>
<a href='activity-log.html' id='activity-log.html' onclick='window.storeMenuState("activity-log.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Activity Log</a>
</li>

<li class='m-0'>
<a href='basics/worker.html' id='basics/worker.html' onclick='window.storeMenuState("basics/worker.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Worker</a>
</li>

<li class='m-0'>
<a href='basics/publish.html' id='basics/publish.html' onclick='window.storeMenuState("basics/publish.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Publish</a>
</li>

<li class='m-0'>
<a href='basics/i18n.html' id='basics/i18n.html' onclick='window.storeMenuState("basics/i18n.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Internationalization</a>
</li>

<li class='m-0'>
<a href='basics/l10n.html' id='basics/l10n.html' onclick='window.storeMenuState("basics/l10n.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Localization</a>
</li>

<li class='m-0'>
<a href='basics/redirection.html' id='basics/redirection.html' onclick='window.storeMenuState("basics/redirection.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Redirection</a>
</li>

<li class='m-0'>
<a href='basics/custom-page.html' id='basics/custom-page.html' onclick='window.storeMenuState("basics/custom-page.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Custom Pages</a>
</li>

<li class='cursor-default px-4 py-1 truncate break-words w-64 m-0'>Web Application</li>

<li class='m-0'>
<a href='advanced-functions/the-go-html-builder.html' id='advanced-functions/the-go-html-builder.html' onclick='window.storeMenuState("advanced-functions/the-go-html-builder.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>The Go HTML builder</a>
</li>

<li class='m-0'>
<a href='basics/page-func-and-event-func.html' id='basics/page-func-and-event-func.html' onclick='window.storeMenuState("basics/page-func-and-event-func.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Page Func and Event Func</a>
</li>

<li class='m-0'>
<a href='basics/layout-function-and-page-injector.html' id='basics/layout-function-and-page-injector.html' onclick='window.storeMenuState("basics/layout-function-and-page-injector.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Layout Function and Page Injector</a>
</li>

<li class='m-0'>
<a href='vuetify-components/lazy-portals.html' id='vuetify-components/lazy-portals.html' onclick='window.storeMenuState("vuetify-components/lazy-portals.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Lazy Portals</a>
</li>

<li class='m-0'>
<a href='basics/switch-pages-with-push-state.html' id='basics/switch-pages-with-push-state.html' onclick='window.storeMenuState("basics/switch-pages-with-push-state.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Switch Pages with Push State</a>
</li>

<li class='m-0'>
<a href='basics/reload-page-with-a-flash.html' id='basics/reload-page-with-a-flash.html' onclick='window.storeMenuState("basics/reload-page-with-a-flash.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Reload Page with a Flash</a>
</li>

<li class='m-0'>
<a href='basics/partial-refresh-with-portal.html' id='basics/partial-refresh-with-portal.html' onclick='window.storeMenuState("basics/partial-refresh-with-portal.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Partial Refresh with Portal</a>
</li>

<li class='m-0'>
<a href='basics/manipulate-page-url-in-event-func.html' id='basics/manipulate-page-url-in-event-func.html' onclick='window.storeMenuState("basics/manipulate-page-url-in-event-func.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Manipulate Page URL in Event Func</a>
</li>

<li class='m-0'>
<a href='basics/summary-of-event-response.html' id='basics/summary-of-event-response.html' onclick='window.storeMenuState("basics/summary-of-event-response.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Summary of Event Response</a>
</li>

<li class='m-0'>
<a href='basics/web-scope.html' id='basics/web-scope.html' onclick='window.storeMenuState("basics/web-scope.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>web.Scope</a>
</li>

<li class='m-0'>
<a href='basics/event-handling.html' id='basics/event-handling.html' onclick='window.storeMenuState("basics/event-handling.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Event Handling</a>
</li>

<li class='m-0'>
<a href='basics/form-handling.html' id='basics/form-handling.html' onclick='window.storeMenuState("basics/form-handling.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Form Handling</a>
</li>

<li class='cursor-default px-4 py-1 truncate break-words w-64 m-0'>UI Components</li>

<li class='m-0'>
<a href='vuetify-components/basic-inputs.html' id='vuetify-components/basic-inputs.html' onclick='window.storeMenuState("vuetify-components/basic-inputs.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Basic Inputs</a>
</li>

<li class='m-0'>
<a href='vuetify-components/a-taste-of-using-vuetify-in-go.html' id='vuetify-components/a-taste-of-using-vuetify-in-go.html' onclick='window.storeMenuState("vuetify-components/a-taste-of-using-vuetify-in-go.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>A Taste of using Vuetify in Go</a>
</li>

<li class='m-0'>
<a href='vuetify-components/linkage-select.html' id='vuetify-components/linkage-select.html' onclick='window.storeMenuState("vuetify-components/linkage-select.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Linkage Select</a>
</li>

<li class='m-0'>
<a href='components-guide/composite-new-component-with-go.html' id='components-guide/composite-new-component-with-go.html' onclick='window.storeMenuState("components-guide/composite-new-component-with-go.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Composite new Component With Go</a>
</li>

<li class='m-0'>
<a href='components-guide/integrate-a-heavy-vue-component.html' id='components-guide/integrate-a-heavy-vue-component.html' onclick='window.storeMenuState("components-guide/integrate-a-heavy-vue-component.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>Integrate a heavy Vue Component</a>
</li>

<li class='cursor-default px-4 py-1 truncate break-words w-64 m-0'>Appendix</li>

<li class='m-0'>
<a href='appendix/all-demo-examples.html' id='appendix/all-demo-examples.html' onclick='window.storeMenuState("appendix/all-demo-examples.html")' class='inline-block pl-10 pr-4 py-1 truncate break-words w-64 hover:text-blue-400 text-gray-700'>All Demo Examples</a>
</li>
</ul>
</aside>

<main class='flex flex-col w-full bg-white overflow-x-hidden overflow-y-auto'>
<div id='docContentBox' class='flex flex-row w-full'>
<div class='flex flex-grow flex-col w-2/3'>
<div class='flex flex-row'>
<button @click='vars.hideAside = !vars.hideAside' class='w-12 h-12 p-4'>
<div class='w-4 h-4 fill-current text-gray-300'>
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16px" height="16px" viewBox="0 0 16 16" version="1.1">
<g id="surface1">
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 2 12 L 2 11 L 14 11 L 14 12 Z M 2 8.5 L 2 7.5 L 14 7.5 L 14 8.5 Z M 2 5 L 2 4 L 14 4 L 14 5 Z M 2 5 "/>
</g>
</svg>
</div>
</button>
</div>

<div id='docMainBox' class='px-16 pb-12 pt-4 overflow-auto'>
<h1 class='mb-8'>SEO</h1>

<div class='border-t'><h2><a name="introduction" class="anchor" href="#introduction" rel="nofollow" aria-hidden="true"><span class="octicon octicon-link"></span></a>Introduction</h2>

<p>The seo package allows for the management and injection of dynamic data into HTML tags for the purpose of Search Engine Optimization.</p>
<h2><a name="create-a-seo-builder" class="anchor" href="#create-a-seo-builder" rel="nofollow" aria-hidden="true"><span class="octicon octicon-link"></span></a>
Create a SEO builder</h2>

<ul>
<li><p>Create a builder and register global seo by default. <code>db</code> is an instance of gorm.DB</p>

<div class="highlight highlight-go"><pre>builder := seo.NewBuilder(db)
</pre></div></li>

<li><p>The default global seo name is <code>Global SEO</code>, if you want to customize the name, passing the name through <code>WithGlobalSEOName</code> option to the <code>NewBuilder</code> function.</p>

<div class="highlight highlight-go"><pre>builder := seo.NewBuilder(db, seo.WithGlobalSEOName(&#34;My Global SEO&#34;))
</pre></div></li>

<li><p>Turn off the default inherit the upper level SEO data when the current SEO data is missing</p>

<div class="highlight highlight-go"><pre>builder := seo.NewBuilder(db, seo.WithInherited(false))
</pre></div></li>

<li><p>The seo package supports localization. you can pass the localization code you want to support through the <code>WithLocales</code> option to the <code>NewBuilder</code> function. if no localization is set, it defaults to empty.</p>

<div class="highlight highlight-go"><pre>builder := seo.NewBuilder(db, seo.WithLocales(&#39;zh&#39;, &#39;en&#39;, &#39;jp&#39;))
</pre></div></li>

<li><p><strong>You can pass multiple options at once</strong></p>

<div class="highlight highlight-go"><pre>builder := seo.NewBuilder(db, seo.WithLocales(&#39;zh&#39;), seo.WithInherited(false), seo.WithGlobalSEOName(&#34;My Global SEO&#34;))
</pre></div></li>
</ul>
<h2><a name="register-and-remove-seo" class="anchor" href="#register-and-remove-seo" rel="nofollow" aria-hidden="true"><span class="octicon octicon-link"></span></a>
Register and remove SEO</h2>

<p>All registered SEO names are unique, If you have already registered a SEO named <code>Test</code>, attempting to register SEO with the same name <code>Test</code> will cause the program to panic.</p>

<p>The second parameter named <code>model</code> in the <code>RegisterSEO(name string, model ...interface{})</code> method is an
instance of a type that has a field of type <code>Setting</code>. If you pass a  <code>model</code> whose type
does not have such a field, the program will panic.</p>

<p>For Example:</p>

<div class="highlight highlight-go"><pre>builder.RegisterSEO(&#34;Test&#34;)
type Test struct {
    ...
    // doesn&#39;t have a field of type Setting
}
builder.RegisterSEO(&#34;Test&#34;, &amp;Test{}) // will panic
</pre></div>

<p>There are two types of SEOs, one is SEO with model, the other is SEO without model.
if you want to register a no model SEO, you can call RegisterSEO method like this:</p>

<div class="highlight highlight-go"><pre>seoBuilder.RegisterSEO(&#34;About Us&#34;)
</pre></div>

<p>if you want to register a SEO with model, you can call RegisterSEO method like this:</p>

<div class="highlight highlight-go"><pre>seoBuilder.RegisterSEO(&#34;Product&#34;, &amp;Product{})
</pre></div>

<ul>
<li>Remove a SEO</li>
</ul>

<p><strong>NOTE: The global seo cannot be removed</strong></p>

<div class="highlight highlight-go"><pre>  builder.RemoveSEO(&amp;Product{}).RemoveSEO(&#34;Not Found&#34;)
</pre></div>

<p>When you remove an SEO, the new parent of its child SEO becomes the parent of the seo.</p>

<ul>
<li>The seo supports hierarchy, and you can use the <code>SetParent</code> or <code>AppendChild</code> methods of <code>SEO</code> to configure the hierarchy.</li>
</ul>

<p><strong>With <code>AppendChildren</code> method</strong></p>

<div class="highlight highlight-go"><pre>  builder.RegisterSEO(&#34;A&#34;).AppendChildren(
      builder.RegisterSEO(&#34;B&#34;),
      builder.RegisterSEO(&#34;C&#34;),
      builder.RegisterSEO(&#34;D&#34;),
  )
</pre></div>

<p><strong>With <code>SetParent</code> Method</strong></p>

<div class="highlight highlight-go"><pre>  seoA := builder.RegisterSEO(&#34;A&#34;)
  builder.RegisterSEO(&#34;B&#34;).SetParent(seoA)
  builder.RegisterSEO(&#34;C&#34;).SetParent(seoA)
  builder.RegisterSEO(&#34;D&#34;).SetParent(seoA)
</pre></div>

<p>The final seo structure is as follows:</p>

<div class="highlight highlight-txt"><pre>  Global SEO
  ├── A
      ├── B
      └── C
      └── D
</pre></div>
<h2><a name="configure-seo" class="anchor" href="#configure-seo" rel="nofollow" aria-hidden="true"><span class="octicon octicon-link"></span></a>
Configure SEO</h2>

<ul>
<li><p>Register setting variables</p>

<div class="highlight highlight-go"><pre>builder := NewBuilder(dbForTest)
builder.RegisterSEO(&amp;Product{}).
RegisterSettingVariables(&#34;Type&#34;, &#34;Place of Origin&#34;)
</pre></div></li>

<li><p>Register context variables</p>

<div class="highlight highlight-go"><pre>seoBuilder = seo.NewBuilder(db)
seoBuilder.RegisterSEO(&amp;models.Post{}).
  RegisterContextVariable(
      &#34;Title&#34;,
      func(object interface{}, _ *seo.Setting, _ *http.Request) string {
          if article, ok := object.(models.Post); ok {
              return article.Title
          }
          return &#34;&#34;
      },
  )
</pre></div></li>

<li><p>Register Meta Property</p>

<div class="highlight highlight-go"><pre>seoBuilder = seo.NewBuilder(db)
seoBuilder.RegisterSEO(&amp;models.Post{}).
  RegisterMetaProperty(
      &#34;og:audio&#34;,
      func(object interface{}, _ *seo.Setting, _ *http.Request) string {
          if article, ok := object.(models.Post); ok {
              return article.audio
          }
          return &#34;&#34;
      },
  )
</pre></div></li>
</ul>
<h2><a name="render-seo-html-data" class="anchor" href="#render-seo-html-data" rel="nofollow" aria-hidden="true"><span class="octicon octicon-link"></span></a>
Render SEO html data</h2>
<h3><a name="render-a-single-seo" class="anchor" href="#render-a-single-seo" rel="nofollow" aria-hidden="true"><span class="octicon octicon-link"></span></a>
Render a single seo</h3>
<h4><a name="render-a-single-seo-with-model" class="anchor" href="#render-a-single-seo-with-model" rel="nofollow" aria-hidden="true"><span class="octicon octicon-link"></span></a>
Render a single seo with model</h4>

<p>To call <code>Render(obj interface{}, req *http.Request)</code> for rendering a single seo.</p>

<p>NOTE: <code>obj</code> must be of type <code>*NameObj</code> or a pointer to a struct that has a field of type <code>Setting</code>.</p>

<div class="highlight highlight-go"><pre>  type Post struct {
      Title   string
      Author  string
      Content string
      SEO     Setting
  }

  post := &amp;Post{
      Title:   &#34;TestRender&#34;,
      Author:  &#34;iBakuman&#34;,
      Content: &#34;Hello, Qor5 SEO&#34;,
      SEO: Setting{
          Title:            &#34;{.{Title}}&#34;,
          Description:      &#34;post for testing&#34;,
          EnabledCustomize: true,
      },
  }
  builder := NewBuilder(dbForTest)
  builder.RegisterSEO(&amp;Post{}).RegisterContextVariable(
      &#34;Title&#34;,
      func(post interface{}, _ *Setting, _ *http.Request) string {
          if p, ok := post.(*Post); ok {
              return p.Title
          }
          return &#34;No title&#34;
      },
  ).RegisterMetaProperty(&#34;og:title&#34;,
      func(post interface{}, _ *Setting, _ *http.Request) string {
          return &#34;Title for og:title&#34;
      },
  )

  defaultReq, _ := http.NewRequest(&#34;POST&#34;, &#34;http://www.demo.qor5.com&#34;, nil)
  res, err := builder.Render(post, defaultReq).MarshalHTML(context.TODO())
  if err != nil {
      panic(err)
  }
  fmt.Println(string(res))
</pre></div>

<p>The output of the above code is as follows:</p>

<div class="highlight highlight-txt"><pre>  &lt;title&gt;TestRender&lt;/title&gt;

  &lt;meta name=&#39;description&#39; content=&#39;post for testing&#39;&gt;

  &lt;meta name=&#39;keywords&#39;&gt;

  &lt;meta property=&#39;og:description&#39; name=&#39;og:description&#39;&gt;

  &lt;meta property=&#39;og:url&#39; name=&#39;og:url&#39;&gt;

  &lt;meta property=&#39;og:type&#39; name=&#39;og:type&#39; content=&#39;website&#39;&gt;

  &lt;meta property=&#39;og:image&#39; name=&#39;og:image&#39;&gt;

  &lt;meta property=&#39;og:title&#39; name=&#39;og:title&#39; content=&#39;Title for og:title&#39;&gt;
</pre></div>
<h4><a name="render-a-single-seo-without-model" class="anchor" href="#render-a-single-seo-without-model" rel="nofollow" aria-hidden="true"><span class="octicon octicon-link"></span></a>
Render a single seo without model</h4>

<div class="highlight highlight-go"><pre>    seoBuilder.Render(&amp;NameObj{&#34;About US&#34;, Locale: &#34;en&#34;})
</pre></div>
<h3><a name="render-multiple-seos-at-once" class="anchor" href="#render-multiple-seos-at-once" rel="nofollow" aria-hidden="true"><span class="octicon octicon-link"></span></a>
Render multiple SEOs at once</h3>
<h4><a name="render-multiple-seos-with-model" class="anchor" href="#render-multiple-seos-with-model" rel="nofollow" aria-hidden="true"><span class="octicon octicon-link"></span></a>
Render multiple SEOs with model</h4>

<p>To call <code>BatchRender(objs []interface, req *http.Request)</code> for batch rendering.</p>

<p><strong>NOTE: You need to ensure that all elements in <code>objs</code> are of the same type.</strong></p>

<p><code>BatchRender</code> does not check if all elements in <code>objs</code> are of the same type, as this can be performance-intensive. Therefore,
  it is the responsibility of the caller to ensure that all elements in <code>objs</code> are of the same type.
  If you pass <code>objs</code> with various types of SEO, it will only take the type of the first element as the standard to obtain the SEO configuration used in the rendering process.</p>

<div class="highlight highlight-go"><pre>  type Post struct {
      Title   string
      Author  string
      Content string
      SEO     Setting
      l10n.Locale
  }

  posts := []interface{}{
      &amp;Post{
          Title:   &#34;TestRenderA&#34;,
          Author:  &#34;iBakuman&#34;,
          Content: &#34;Hello, Qor5 SEO&#34;,
          SEO: Setting{
              Title:            &#34;{.{Title}}&#34;,
              Description:      &#34;postA for testing&#34;,
              EnabledCustomize: true,
          },
      },
      &amp;Post{
          Title:   &#34;TestB&#34;,
          Author:  &#34;iBakuman&#34;,
          Content: &#34;Hello, Qor5 SEO&#34;,
          SEO: Setting{
              Title:            &#34;{.{Title}}&#34;,
              Description:      &#34;postB for testing&#34;,
              EnabledCustomize: true,
          },
      },
  }
  builder := NewBuilder(dbForTest, WithLocales(&#34;en&#34;))
  builder.RegisterSEO(&amp;Post{}).RegisterContextVariable(
      &#34;Title&#34;,
      func(post interface{}, _ *Setting, _ *http.Request) string {
          if p, ok := post.(*Post); ok {
              return p.Title
          }
          return &#34;No title&#34;
      },
  ).RegisterMetaProperty(&#34;og:title&#34;,
      func(post interface{}, _ *Setting, _ *http.Request) string {
          return &#34;Title for og:title&#34;
      },
  )

  defaultReq, _ := http.NewRequest(&#34;POST&#34;, &#34;http://www.demo.qor5.com&#34;, nil)
  SEOs := builder.BatchRender(posts, defaultReq)
  for _, seo := range SEOs {
      html, _ := seo.MarshalHTML(context.TODO())
      fmt.Println(string(html))
      fmt.Println(&#34;----------------------------&#34;)
  }
</pre></div>

<p>The output of the above code is as follows:</p>

<div class="highlight highlight-txt"><pre>  &lt;title&gt;TestRenderA&lt;/title&gt;

  &lt;meta name=&#39;description&#39; content=&#39;postA for testing&#39;&gt;

  &lt;meta name=&#39;keywords&#39;&gt;

  &lt;meta property=&#39;og:description&#39; name=&#39;og:description&#39;&gt;

  &lt;meta property=&#39;og:url&#39; name=&#39;og:url&#39;&gt;

  &lt;meta property=&#39;og:type&#39; name=&#39;og:type&#39; content=&#39;website&#39;&gt;

  &lt;meta property=&#39;og:image&#39; name=&#39;og:image&#39;&gt;

  &lt;meta property=&#39;og:title&#39; name=&#39;og:title&#39; content=&#39;Title for og:title&#39;&gt;

  ----------------------------

  &lt;title&gt;TestB&lt;/title&gt;

  &lt;meta name=&#39;description&#39; content=&#39;postB for testing&#39;&gt;

  &lt;meta name=&#39;keywords&#39;&gt;

  &lt;meta property=&#39;og:url&#39; name=&#39;og:url&#39;&gt;

  &lt;meta property=&#39;og:type&#39; name=&#39;og:type&#39; content=&#39;website&#39;&gt;

  &lt;meta property=&#39;og:image&#39; name=&#39;og:image&#39;&gt;

  &lt;meta property=&#39;og:title&#39; name=&#39;og:title&#39; content=&#39;Title for og:title&#39;&gt;

  &lt;meta property=&#39;og:description&#39; name=&#39;og:description&#39;&gt;

  ----------------------------
</pre></div>
<h4><a name="render-multiple-seos-without-model" class="anchor" href="#render-multiple-seos-without-model" rel="nofollow" aria-hidden="true"><span class="octicon octicon-link"></span></a>
Render multiple SEOs without model</h4>

<div class="highlight highlight-go"><pre>  seoBuilder.BatchRender(NewNonModelSEOSlice(&#34;Product&#34;, &#34;en&#34;, &#34;zh&#34;))
</pre></div>
</div>
</div>
</div>

<div class='font-medium text-base hidden xl:block text-gray-600 pt-4'>
<div class='sticky top-4 w-52'>On This Page<toc></toc></div>
</div>
</div>
<search-result></search-result></main>
</div>
</div>
</div>
</div>
</body>
</html>
